package com.lyy.mybatis.sensitive.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import lombok.experimental.UtilityClass;

import java.util.Collection;
import java.util.Random;
import java.util.stream.Collectors;

/**
 * 将62进制的字符串　加解密　成新的62进制字符串
 */
@UtilityClass
public class Secret62Util {
    /**
     * 62个字符(数字＋小写字母＋大写字母)　顺序字符
     */
    public static final String LETTER62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    /**
     * 62个字符(数字＋小写字母＋大写字母)　混淆乱序字符
     *
     * @see #scramble(String str, long seed) 可使用该方法来生成混淆字符　以下默认为scramble(LETTER62,111)生成的混淆字符
     */
    public static final String LETTER62_SHUFFLE = "hrpvsGASbkE9zmotaLqZg3wBx4VIRF6JQ02dfeWc51CnTljDyOX7NHYPuUiKM8";

    /**
     * 加密特定标识
     */
    public static final char prefix = '#';

    /**
     * 将源字符串进行混淆，混淆后返回新的字符串。
     *
     * @param str  　要混淆的字符串
     * @param seed 　混淆的种子
     * @return String 新的字符
     */
    public String scramble(String str, long seed) {
        char[] charArray = str.toCharArray();
        Random random = new Random(seed);

        // 遍历字符数组
        for (int i = 0; i < charArray.length; i++) {
            int randomIndex = random.nextInt(charArray.length);
            // 交换当前字符与随机字符
            char temp = charArray[i];
            charArray[i] = charArray[randomIndex];
            charArray[randomIndex] = temp;
        }
        return new String(charArray);
    }

    /**
     * 加密-无特定前缀
     *
     * @param input 输入的字符串
     * @return 加密后的字符串
     */
    public String encryptWithOutPrefix(String input) {
        if (StrUtil.isBlank(input)) {
            return input;
        }
        StringBuilder encrypted = new StringBuilder();
        for (char c : input.toCharArray()) {
            int index = LETTER62.indexOf(c);
            Assert.isTrue(index >= 0, "非法字符");
            char encryptChar = LETTER62_SHUFFLE.charAt(index);
            encrypted.append(encryptChar);
        }
        return encrypted.toString();
    }


    /**
     * 加密-带特定前缀
     *
     * @param input 输入的字符串
     * @return 加密后的字符串
     * @see #prefix
     */
    public String encrypt(String input) {
        if (StrUtil.isBlank(input)) {
            return input;
        }
        // 如果待加密字段已包含特定加密标识,则直接返回
        if (StrUtil.startWith(input, prefix)) {
            return input;
        }
        return prefix + encryptWithOutPrefix(input);
    }

    /**
     * 解密
     *
     * @param input 输入的字符串
     * @return 解密后的字符串
     */
    public String decrypt(String input) {
        if (StrUtil.isBlank(input)) {
            return input;
        }
        // 如果待解密字符串没有特定加密标识,则直接返回
        if (!StrUtil.startWith(input, prefix)) {
            return input;
        }
        StringBuilder decrypted = new StringBuilder();
        for (char c : input.toCharArray()) {
            if (c == prefix) {
                continue;
            }
            int index = LETTER62_SHUFFLE.indexOf(c);
            Assert.isTrue(index >= 0, "非法字符");
            char decryptChar = LETTER62.charAt(index);
            decrypted.append(decryptChar);
        }
        return decrypted.toString();
    }

    /**
     * 将集合中的元素进行加密，返回新的集合。
     *
     * @param collection
     * @return
     */
    public Collection<String> arrayToEncrypts(Collection<String> collection) {
        if (CollUtil.isEmpty(collection)) {
            return collection;
        }
        return collection.stream().map(Secret62Util::encrypt).collect(Collectors.toList());
    }

    /**
     * 检查字符串内的字符不可重复,重复直接会抛出异常
     *
     * @param str 要检查的字符串
     * @throws IllegalArgumentException 字符串内字符不可重复
     */
    public void check(String str) {
        int length = str.length();
        for (int i = 0; i < length; i++) {
            char c = str.charAt(i);
            for (int j = i + 1; j < length; j++) {
                if (c == str.charAt(j)) {
                    throw new IllegalArgumentException("字符串内字符不可重复");
                }
            }
        }
    }

}
